//#########################################################################
//                              WackiEngie
//           Copyright (c) 2014 Marc Wacker. All rights reserved.
//#########################################################################

#include "WeCorePrerequisites.h"
#include "WeBaseApplication.h"
#include "WeApplication.h"
#include "WeInputEvents.h"
#include "WePlatform.h"
#include "WeInput.h"
#include "WeTime.h"
#include "WeDebug.h"
#include "WeShaderProg.h"
#include "WeVector3.h"
#include "WeMatrix4.h"
#include "WeMath.h"

#include "WeImage.h"
#include "WeTexture2D.h"

using namespace WackiEngine;

class MyApplication : public Application
{
public:
	MyApplication()
		: Application(1280, 800, "MyApplication"),
		_testShader("../../resources/shaders/default.vert", "../../resources/shaders/default.frag") // TODO: update shader implementation to be able to load with just the load function
	{
		// connect input callbacks
		gInput().keyInput.connect(this, &MyApplication::onKey);
		gInput().characterInput.connect(this, &MyApplication::onCharInput);
		gInput().mouseMove.connect(this, &MyApplication::onMouseMove);
		gInput().mouseDown.connect(this, &MyApplication::onMouseDown);
		gInput().mouseUp.connect(this, &MyApplication::onMouseUp);
		gInput().mouseDoubleClick.connect(this, &MyApplication::onMouseDoubleClick);
		gInput().scrollWheel.connect(this, &MyApplication::onMouseWheel);

		_proj.ortho(0.0f, 1280.0f, 0.0f, 800.0f, -1.0f, 1.0f);


		Image testTex("../../resources/images/xboxleftstick.png");
		_leftStickTex.image(0, GL_RGBA, testTex.width(), testTex.height(), testTex.srcDataFormat(), testTex.srcDataType(), testTex.data());
		_leftStickTex.setMinFilter(GL_NEAREST);
		_leftStickTex.setMagFilter(GL_NEAREST);
	}

	void update()
	{
		// temp test for axis
		/*if(gInput().getAxis(KC_Gamepad_LeftX) != 0.0f || gInput().getAxis(KC_Gamepad_LeftY) != 0.0f)
			LOGINFO("LeftStick: " + toString(gInput().getAxis(KC_Gamepad_LeftX)) + ", " + toString(gInput().getAxis(KC_Gamepad_LeftY)));
		if(gInput().getAxis(KC_Gamepad_RightX) != 0.0f || gInput().getAxis(KC_Gamepad_RightY) != 0.0f)
			LOGINFO("RightStick: " + toString(gInput().getAxis(KC_Gamepad_RightX)) + ", " + toString(gInput().getAxis(KC_Gamepad_RightY)));
		if(gInput().getAxis(KC_Gamepad_LeftTriggerAxis) > 0.0f)
			LOGINFO("LeftTrigger: " + toString(gInput().getAxis(KC_Gamepad_LeftTriggerAxis)));
		if(gInput().getAxis(KC_Gamepad_RightTriggerAxis) > 0.0f)
			LOGINFO("RightTrigger: " + toString(gInput().getAxis(KC_Gamepad_RightTriggerAxis)));*/


		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// render position of left stick
		int32 slices = 16;
		float sliceDeg = (float)360 / slices;
		if(!_leftStickPos) {

			Vector<Vector3f>	verts;
			Vector<Vector2f>	texcoords;
			Vector<ushort>		indices;

			// build circle
			for(int i = 0; i < slices + 1; ++i) {
				float curDeg = i * sliceDeg;
				// verts
				float x = cos(WE_DEG2RAD * curDeg);
				float y = sin(WE_DEG2RAD * curDeg);
				verts.push_back(Vector3f(x, y, 0.0f));
				
				// texcoords
				texcoords.push_back(Vector2f(x * 0.5f + 0.5f, y * 0.5f + 0.5f));

				// indices
				if(i < slices) {
					indices.push_back(i);
					indices.push_back((i + 1));
					indices.push_back(slices + 1);
				}
			}

			// add center point
			verts.push_back(Vector3f(0.0f, 0.0f, 0.0f));
			texcoords.push_back(Vector2f(0.5f, 0.5f));

			glGenBuffers(1, &_leftStickPos);
			glBindBuffer(GL_ARRAY_BUFFER, _leftStickPos);
			glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(Vector3f), &verts[0], GL_STATIC_DRAW);

			glGenBuffers(1, &_leftStickTexcoord);
			glBindBuffer(GL_ARRAY_BUFFER, _leftStickTexcoord);
			glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(Vector2f), &texcoords[0], GL_STATIC_DRAW);

			glGenBuffers(1, &_leftStickIndex);
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _leftStickIndex);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(ushort), &indices[0], GL_STATIC_DRAW);
		}

		_testShader.beginUse();

		_modelView.identity();
		_modelView.scale(20, 20, 0);
		_modelView.translation(gInput().getAxis(KC_Gamepad_LeftX)*100.0f + 300.0f, gInput().getAxis(KC_Gamepad_LeftY)*100.0f + 300.0f, 0.0f);

		glActiveTexture(GL_TEXTURE0 + _leftStickTex.handle());
		_leftStickTex.bind();

		_mvp = _proj * _modelView;
		_testShader.uniformMatrix4fv("mvp", 1, _mvp.m(), true);
		_testShader.uniform1i("texture", _leftStickTex.handle());

		glBindBuffer(GL_ARRAY_BUFFER, _leftStickPos);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

		glBindBuffer(GL_ARRAY_BUFFER, _leftStickTexcoord);
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _leftStickIndex);

		glDrawElements(GL_TRIANGLES, slices * 3, GL_UNSIGNED_SHORT, (void*)0);

		glDisableVertexAttribArray(0);
		glDisableVertexAttribArray(1);

		_testShader.endUse();


		// temporary fps display to see performance problems
		static float fpsSamples[20];
		static int fpsIndex = 0;

		fpsSamples[fpsIndex] = 1.0f/gTime().getFrameDelta();

		fpsIndex++;
		fpsIndex %= 20;

		_fps = 0;
		for(int i = 0; i < 20; ++i)
			_fps += fpsSamples[i];
		_fps *= 0.05f;

		gPlatform().setTitle(toString(_fps));
	}


	void onKey(const KeyEvent& e)
	{
		/*/
		String keyName;
		switch(e.key) {

		case KC_Undefined: keyName = "Undefined"; break;
		case KC_Backspace: keyName = "Backspace"; break;
		case KC_Delete: keyName = "Delete"; break;
		case KC_Tab: keyName = "Tab"; break;
		case KC_Clear: keyName = "Clear"; break;
		case KC_Enter: keyName = "Enter"; break;
		case KC_Pause: keyName = "Pause"; break;
		case KC_Escape: keyName = "Escape"; break;
		case KC_Space: keyName = "Space"; break;
		case KC_Numpad0: keyName = "Numpad0"; break;
		case KC_Numpad1: keyName = "Numpad1"; break;
		case KC_Numpad2: keyName = "Numpad2"; break;
		case KC_Numpad3: keyName = "Numpad3"; break;
		case KC_Numpad4: keyName = "Numpad4"; break;
		case KC_Numpad5: keyName = "Numpad5"; break;
		case KC_Numpad6: keyName = "Numpad6"; break;
		case KC_Numpad7: keyName = "Numpad7"; break;
		case KC_Numpad8: keyName = "Numpad8"; break;
		case KC_Numpad9: keyName = "Numpad9"; break;
		case KC_NumpadPeriod: keyName = "NumpadPeriod"; break;
		case KC_NumpadDivide: keyName = "NumpadDivide"; break;
		case KC_NumpadMultiply: keyName = "NumpadMultiply"; break;
		case KC_NumpadMinus: keyName = "NumpadMinus"; break;
		case KC_NumpadPlus: keyName = "NumpadPlus"; break;
		case KC_NumpadEnter: keyName = "NumpadEnter"; break;
		case KC_NumpadEquals: keyName = "NumpadEquals"; break;
		case KC_UpArrow: keyName = "UpArrow"; break;
		case KC_DownArrow: keyName = "DownArrow"; break;
		case KC_RightArrow: keyName = "RightArrow"; break;
		case KC_LeftArrow: keyName = "LeftArrow"; break;
		case KC_Insert: keyName = "Insert"; break;
		case KC_Home: keyName = "Home"; break;
		case KC_End: keyName = "End"; break;
		case KC_PageUp: keyName = "PageUp"; break;
		case KC_PageDown: keyName = "PageDown"; break;
		case KC_F1: keyName = "F1"; break;
		case KC_F2: keyName = "F2"; break;
		case KC_F3: keyName = "F3"; break;
		case KC_F4: keyName = "F4"; break;
		case KC_F5: keyName = "F5"; break;
		case KC_F6: keyName = "F6"; break;
		case KC_F7: keyName = "F7"; break;
		case KC_F8: keyName = "F8"; break;
		case KC_F9: keyName = "F9"; break;
		case KC_F10: keyName = "F10"; break;
		case KC_F11: keyName = "F11"; break;
		case KC_F12: keyName = "F12"; break;
		case KC_F13: keyName = "F13"; break;
		case KC_F14: keyName = "F14"; break;
		case KC_F15: keyName = "F15"; break;
		case KC_0: keyName = "0"; break;
		case KC_1: keyName = "1"; break;
		case KC_2: keyName = "2"; break;
		case KC_3: keyName = "3"; break;
		case KC_4: keyName = "4"; break;
		case KC_5: keyName = "5"; break;
		case KC_6: keyName = "6"; break;
		case KC_7: keyName = "7"; break;
		case KC_8: keyName = "8"; break;
		case KC_9: keyName = "9"; break;
		case KC_Exclaim: keyName = "Exclaim"; break;
		case KC_DoubleQuote: keyName = "DoubleQuote"; break;
		case KC_Hash: keyName = "Hash"; break;
		case KC_Dollar: keyName = "Dollar"; break;
		case KC_Ampersand: keyName = "Ampersand"; break;
		case KC_Apostrophe: keyName = "Apostrophe"; break;
		case KC_LeftParen: keyName = "LeftParen"; break;
		case KC_RightParen: keyName = "RightParen"; break;
		case KC_Asterisk: keyName = "Asterisk"; break;
		case KC_Plus: keyName = "Plus"; break;
		case KC_Comma: keyName = "Comma"; break;
		case KC_Minus: keyName = "Minus"; break;
		case KC_Period: keyName = "Period"; break;
		case KC_Slash: keyName = "Slash"; break;
		case KC_Colon: keyName = "Colon"; break;
		case KC_Semicolon: keyName = "Semicolon"; break;
		case KC_Less: keyName = "Less"; break;
		case KC_Equals: keyName = "Equals"; break;
		case KC_Greater: keyName = "Greater"; break;
		case KC_Question: keyName = "Question"; break;
		case KC_At: keyName = "At"; break;
		case KC_LeftBracket: keyName = "LeftBracket"; break;
		case KC_Backslash: keyName = "Backslash"; break;
		case KC_RightBracket: keyName = "RightBracket"; break;
		case KC_Caret: keyName = "Caret"; break;
		case KC_Underscore: keyName = "Underscore"; break;
		case KC_BackQuote: keyName = "BackQuote"; break;
		case KC_A: keyName = "A"; break;
		case KC_B: keyName = "B"; break;
		case KC_C: keyName = "C"; break;
		case KC_D: keyName = "D"; break;
		case KC_E: keyName = "E"; break;
		case KC_F: keyName = "F"; break;
		case KC_G: keyName = "G"; break;
		case KC_H: keyName = "H"; break;
		case KC_I: keyName = "I"; break;
		case KC_J: keyName = "J"; break;
		case KC_K: keyName = "K"; break;
		case KC_L: keyName = "L"; break;
		case KC_M: keyName = "M"; break;
		case KC_N: keyName = "N"; break;
		case KC_O: keyName = "O"; break;
		case KC_P: keyName = "P"; break;
		case KC_Q: keyName = "Q"; break;
		case KC_R: keyName = "R"; break;
		case KC_S: keyName = "S"; break;
		case KC_T: keyName = "T"; break;
		case KC_U: keyName = "U"; break;
		case KC_V: keyName = "V"; break;
		case KC_W: keyName = "W"; break;
		case KC_X: keyName = "X"; break;
		case KC_Y: keyName = "Y"; break;
		case KC_Z: keyName = "Z"; break;
		case KC_Numlock: keyName = "Numlock"; break;
		case KC_CapsLock: keyName = "CapsLock"; break;
		case KC_ScrollLock: keyName = "ScrollLock"; break;
		case KC_RightShift: keyName = "RightShift"; break;
		case KC_LeftShift: keyName = "LeftShift"; break;
		case KC_RightControl: keyName = "RightControl"; break;
		case KC_LeftControl: keyName = "LeftControl"; break;
		case KC_RightAlt: keyName = "RightAlt"; break;
		case KC_LeftAlt: keyName = "LeftAlt"; break;
		case KC_LeftCommand: keyName = "LeftCommand"; break;
		case KC_RightCommand: keyName = "RightCommand"; break;
		case KC_AltGr: keyName = "AltGr"; break;
		case KC_Help: keyName = "Help"; break;
		case KC_Print: keyName = "Print"; break;
		case KC_SysReq: keyName = "SysReq"; break;
		case KC_Break: keyName = "Break"; break;
		case KC_Menu: keyName = "Menu"; break;

			//mouse axes
		case KC_Mouse_X: keyName = "Mouse_X"; break;
		case KC_Mouse_Y: keyName = "Mouse_Y"; break;


			//mouse buttons	//Gamepad face buttons (example: X, Y, A, B on xbox)
		case KC_Mouse_Left: keyName = "Mouse Left"; break;
		case KC_Mouse_Right: keyName = "Mouse Right"; break;
		case KC_Mouse_Middle: keyName = "Mouse Middle"; break;
		case KC_Mouse_Btn4: keyName = "Mouse Btn4"; break;
		case KC_Mouse_Btn5: keyName = "Mouse Btn5"; break;
		case KC_Mouse_Btn6: keyName = "Mouse Btn6"; break;
		case KC_Mouse_Btn7: keyName = "Mouse Btn7"; break;
		case KC_Mouse_Btn8: keyName = "Mouse Btn8"; break;
		case KC_Mouse_Btn9: keyName = "Mouse Btn9"; break;
		case KC_Mouse_Btn10: keyName = "Mouse Btn10"; break;
		case KC_Mouse_Btn11: keyName = "Mouse Btn11"; break;
		case KC_Mouse_Btn12: keyName = "Mouse Btn12"; break;
		case KC_Mouse_Btn13: keyName = "Mouse Btn13"; break;
		case KC_Mouse_Btn14: keyName = "Mouse Btn14"; break;
		case KC_Mouse_Btn15: keyName = "Mouse Btn15"; break;
		case KC_Mouse_Btn16: keyName = "Mouse Btn16"; break;
		case KC_Mouse_Btn17: keyName = "Mouse Btn17"; break;
		case KC_Mouse_Btn18: keyName = "Mouse Btn18"; break;
		case KC_Mouse_Btn19: keyName = "Mouse Btn19"; break;
		case KC_Mouse_Btn20: keyName = "Mouse Btn20"; break;
		case KC_Mouse_Btn21: keyName = "Mouse Btn21"; break;

			//mouse wheel virtual key codes for key emulation
		case KC_Mouse_WheelUp: keyName = "Mouse WheelUp"; break;
		case KC_Mouse_WheelDown: keyName = "Mouse WheelDown"; break;
		case KC_Mouse_WheelLeft: keyName = "Mouse WheelLeft"; break;
		case KC_Mouse_WheelRight: keyName = "Mouse WheelRight"; break;

			// Gamepad axes
		case KC_Gamepad_LeftX: keyName = "Gamepad_LeftX"; break;
		case KC_Gamepad_LeftY: keyName = "Gamepad_LeftY"; break;
		case KC_Gamepad_RightX: keyName = "Gamepad_RightX"; break;
		case KC_Gamepad_RightY: keyName = "Gamepad_RightY"; break;
		case KC_Gamepad_LeftTriggerAxis: keyName = "Gamepad_LeftTriggerAxis"; break;
		case KC_Gamepad_RightTriggerAxis: keyName = "Gamepad_RightTriggerAxis"; break;


			// Gamepad buttons
		case KC_Gamepad_LeftThumbstick: keyName = "Gamepad LeftThumbstick"; break;
		case KC_Gamepad_RightThumbstick: keyName = "Gamepad RightThumbstick"; break;
		case KC_Gamepad_SpecialLeft: keyName = "Gamepad SpecialLeft"; break;
		case KC_Gamepad_SpecialRight: keyName = "Gamepad SpecialRight"; break;
		case KC_Gamepad_FaceButtonBottom: keyName = "Gamepad FaceButtonBottom"; break;
		case KC_Gamepad_FaceButtonRight: keyName = "Gamepad FaceButtonRight"; break;
		case KC_Gamepad_FaceButtonTop: keyName = "Gamepad FaceButtonTop"; break;
		case KC_Gamepad_FaceButtonLeft: keyName = "Gamepad FaceButtonLeft"; break;
		case KC_Gamepad_LeftShoulder: keyName = "Gamepad LeftShoulder"; break;
		case KC_Gamepad_RightShoulder: keyName = "Gamepad RightShoulder"; break;
		case KC_Gamepad_LeftTrigger: keyName = "Gamepad LeftTrigger"; break;
		case KC_Gamepad_RightTrigger: keyName = "Gamepad RightTrigger"; break;
		case KC_Gamepad_DPadUp: keyName = "Gamepad DPadUp"; break;
		case KC_Gamepad_DPadDown: keyName = "Gamepad DPadDown"; break;
		case KC_Gamepad_DPadLeft: keyName = "Gamepad DPadLeft"; break;
		case KC_Gamepad_DPadRight: keyName = "Gamepad DPadRight"; break;


			//virtual key codes used for input axis button press/release emulation
		case KC_Gamepad_LeftStickUp: keyName = "Gamepad (virtual) LeftStickUp"; break;
		case KC_Gamepad_LeftStickDown: keyName = "Gamepad (virtual) LeftStickDown"; break;
		case KC_Gamepad_LeftStickLeft: keyName = "Gamepad (virtual) LeftStickLeft"; break;
		case KC_Gamepad_LeftStickRight: keyName = "Gamepad (virtual) LeftStickRight"; break;
		case KC_Gamepad_RightStickUp: keyName = "Gamepad (virtual) RightStickUp"; break;
		case KC_Gamepad_RightStickDown: keyName = "Gamepad (virtual) RightStickDown"; break;
		case KC_Gamepad_RightStickLeft: keyName = "Gamepad (virtual) RightStickLeft"; break;
		case KC_Gamepad_RightStickRight: keyName = "Gamepad (virtual) RightStickRight"; break;
		default: keyName = std::to_string(e.key); break;
		}


		switch(e.type) {
		case KE_Press: LOGINFO("Key Press: " + keyName); break;
		case KE_Release: LOGINFO("Key Release: " + keyName); break;
		case KE_Repeat: LOGINFO("Key Repeat: " + keyName); break;
		case KE_DoubleClick: LOGINFO("Key Doubleclick: " + keyName); break;
		}
		/**/
	}

	void onCharInput(const CharInputEvent& e)
	{
		//std::wcout << "char input cb: " << (wchar_t)(e.textCharacter) << " " << e.textCharacter << std::endl;
		//LOGWARNING("herro");
	}

	void onMouseMove(const PointerEvent& e)
	{
		//std::cout << "mouse move cb: " << e.x << ", " << e.y << ", " << e.deltaX << ", " << e.deltaY << ", " << e.button << ", " << e.mods << std::endl;
	}

	void onMouseDown(const PointerEvent& e)
	{
		//std::cout << "mouse down cb: " << e.x << ", " << e.y << ", " << e.deltaX << ", " << e.deltaY << ", " << e.button << ", " << e.mods << std::endl;
	}

	void onMouseUp(const PointerEvent& e)
	{
		//std::cout << "mouse up cb: " << e.x << ", " << e.y << ", " << e.deltaX << ", " << e.deltaY << ", " << e.button << ", " << e.mods << std::endl;
	}

	void onMouseDoubleClick(const PointerEvent& e)
	{
		//std::cout << "mouse double click cb: " << e.x << ", " << e.y << ", " << e.deltaX << ", " << e.deltaY << ", " << e.button << ", " << e.mods << std::endl;
	}

	void onMouseWheel(const PointerEvent& e)
	{
		//std::cout << "mouse wheel cb: " << e.x << ", " << e.y << ", " << e.deltaX << ", " << e.deltaY << ", " << e.button << ", " << e.mods << std::endl;
	}

private:
	ShaderProg	_testShader;
	GLuint		_leftStickPos = 0;
	GLuint		_leftStickTexcoord = 0;
	GLuint		_leftStickIndex = 0;
	Matrix4f	_mvp;
	Matrix4f	_modelView;
	Matrix4f	_proj;
	float		_fps;
	Texture2D	_leftStickTex;
};

void main()
{
	MyApplication app;
	app.run();
}